#pragma once

#include <queue>
#include <vector>
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"

const static int g_thread_num = 5;

template <typename T>
class ThreadPool
{
private:
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    // 线程处理任务
    void Pop(const std::string &name)
    {
        while (true)
        {
            T t;
            {
                // 将临界区资源获取到线程私有栈上
                LockGuard lockguard(&_lock);
                while (QueueIsEmpty() && _running) // 笔记3: 队列为空且线程池运行状态不为false
                {
                    _wait_thread_num++;
                    _cond.Wait(_lock); // 条件变量等待
                    _wait_thread_num--;
                }
                if (!_running && QueueIsEmpty())// 笔记3: 线程池被唤醒，且没有任务执行
                {
                    LOG(LogLevel::INFO) << "线程池退出 && 任务队列为空" << name << "退出";
                    break;
                }
                // 笔记3: 
                // 此时一定有任务
                // 线程池退出就进行消耗历史任务
                // 线程池不退出就正常工作
                t = _q.front(); // 笔记2: 处理任务不需要在临界区中
                _q.pop();
            }
            t(); // 任务执行不需要在临界区中
            LOG(LogLevel::DEBUG) << name << "task: " << t.RetToString();
        }
    }

public:
    ThreadPool(int thread_num = g_thread_num)
        : _running(false),
          _num(thread_num),
          _wait_thread_num(0)
    {
        for (int i = 0; i < _num; ++i)
        {
            // 笔记1: 此时ThreadPool对象已经存在了,就可以访问类内属性和方法
            std::string name = "thread-" + std::to_string(i + 1);

            // 方法1:
            //  auto f = std::bind(Routine, this);
            //  Thread t(f, name);
            //  _threads.push_back(std::move(t));

            // 方法2:
            //  Thread t([this](const std::string &name){
            //      this->Routine();
            //  }, name);//匿名函数,lambda
            //  _threads.push_back(std::move(t));

            // 方法3:
            _threads.emplace_back([this](const std::string &name)
                                  { this->Pop(name); }, name);
        }
        LOG(LogLevel::INFO) << "create threadpool success";
    }

    void Start()
    {
        if (_running)
            return;
        _running = true;
        for (auto &t : _threads)
        {
            t.Start(); // 线程启动
        }
        LOG(LogLevel::INFO) << "Start threadpool success";
    }

    //  笔记3: 线程按照正常唤醒逻辑退出
    //  情况: 
    //  1、线程池被唤醒，且没有任务执行 == 线程池退出
    //  2、线程池被唤醒，且有任务执行 == 线程池不会立即退出，要等任务完成再退出
    //  3、线程没有休眠，将它要执行的任务都执行完再退出
    //  队列中有任务的情况下，就不会休眠
    void Stop()
    {
        //简单版: 不推荐
        // if (!_running)
        //     return;
        // _running = false;
        // for (auto &t : _threads)
        // {
        //     t.Stop(); // 线程暂停
        // }

        if (!_running)
            return;
        _running = false;
        if (_wait_thread_num) // 有线程在休眠就全部唤醒
            _cond.NotifyAll();

        LOG(LogLevel::INFO) << "Stop threadpool success";
    }

    void Wait()
    {
        for (auto &t : _threads)
        {
            t.Join(); // 线程等待
        }
        LOG(LogLevel::INFO) << "Wait threadpool success";
    }

    // 将任务写入任务队列
    void Push(const T &in)
    {
        if (!_running)
            return;
        {
            LockGuard lockguard(&_lock);

            _q.push(in);
            if (_wait_thread_num > 0)
                _cond.NotifyOne();
        }
    }

    ~ThreadPool()
    {
    }

private:
    std::queue<T> _q;             // 任务队列
    std::vector<Thread> _threads; // 多个线程
    int _num;                     // 线程数

    int _wait_thread_num; // 等待的线程数

    Mutex _lock;
    Cond _cond;

    bool _running; // 运行状态
};